#include "application.h"

#define C_VIDEO_DECODE_STACK_SIZE	512
#define C_VIDEO_Q_ACCEPT_MAX		5

/*os task stack */
INT32U		video_decode_stack[C_VIDEO_DECODE_STACK_SIZE];

/* os task queue */
OS_EVENT	*vid_dec_q;
void		*vid_dec_q_buffer[C_VIDEO_Q_ACCEPT_MAX];
OS_EVENT	*vid_dec_ack_m;

INT32S video_decode_task_start(void)
{
	INT32S	nRet;

	nRet = STATUS_OK;
	POST_MESSAGE(vid_dec_q, MSG_VID_DEC_START, vid_dec_ack_m, 5000);
Return:
	return nRet;
}

INT32S video_decode_task_restart(void)
{
	INT32S	nRet;

	nRet = STATUS_OK;
	POST_MESSAGE(vid_dec_q, MSG_VID_DEC_RESTART, vid_dec_ack_m, 5000);
Return:
	return nRet;
}

INT32S video_decode_task_stop(void)
{
	INT32S	nRet;

	nRet = STATUS_OK;
	POST_MESSAGE(vid_dec_q, MSG_VID_DEC_STOP, vid_dec_ack_m, 0);
Return:
	return nRet;
}

INT32S video_decode_task_nth_frame(void)
{
	INT32S	nRet;

	nRet = STATUS_OK;
	POST_MESSAGE(vid_dec_q, MSG_VID_DEC_NTH, vid_dec_ack_m, 5000);
Return:
	return nRet;
}

INT32S video_decode_task_create(INT8U proi)
{
	INT8U	err;
	INT32S	nRet;

	DBG_PRINT("%s ... \r\n", __func__);

	vid_dec_q = OSQCreate(vid_dec_q_buffer, C_VIDEO_Q_ACCEPT_MAX);
	if(!vid_dec_q)
		RETURN(STATUS_FAIL);

	vid_dec_ack_m = OSMboxCreate(NULL);
	if(!vid_dec_ack_m)
		RETURN(STATUS_FAIL);

	err = OSTaskCreate(video_decode_task_entry, (void *) NULL, &video_decode_stack[C_VIDEO_DECODE_STACK_SIZE - 1], proi);
	if(err != OS_NO_ERR)
		RETURN(STATUS_FAIL);

	nRet = STATUS_OK;
Return:
	return nRet;
}

INT32S video_decode_task_del(INT8U proi)
{
	INT8U	err;
	INT32S	nRet;

	nRet = STATUS_OK;
	//POST_MESSAGE(vid_dec_q, MSG_VID_DEC_EXIT, vid_dec_ack_m, 5000);
	//----del video decode  task here , save waiting ack time
	OSTaskSuspend(proi);
	OSTimeDly(1);
	OSTaskDelReq(proi);
	OSTaskDel(proi);
//Return:
	OSQFlush(vid_dec_q);
	OSQDel(vid_dec_q, OS_DEL_ALWAYS, &err);
	OSMboxDel(vid_dec_ack_m, OS_DEL_ALWAYS, &err);
	return STATUS_OK;
}

static void video_decode_task_end(void)
{
}

void video_decode_task_entry(void *parm)
{
	OS_INIT_CRITICAL();
	INT16U	tft_w = drv_l2_display_tft_width_get();
	INT16U	tft_h = drv_l2_display_tft_height_get();
	INT8U 	height_offset = drv_l2_display_frame_y_get();
	INT8U	success_flag, init_flag, nth_flag;
	INT8U	err;
	INT32S	status, error_id;
	INT32U	raw_data_addr;
	INT32U	scaler_out_addr;
	INT32U	msg_id;
	INT64S	delta_Tv, temp;
	long	Duration, size;
	DISP_P	display_frame;
	INT32U	display_buf;

	DBG_PRINT("%s ... \r\n", __func__);

	nth_flag = 0;
	while(1)
	{
		msg_id = (INT32U) OSQPend(vid_dec_q, 0, &err);
		if((err != OS_NO_ERR) || !msg_id)
			continue;

		switch(msg_id)
		{
		case MSG_VID_DEC_ONE_FRAME:
			success_flag = 0;
			if(!raw_data_addr || size <= 0)			// read video data speed too slow
			{
				if(MultiMediaParser_IsEOV(p_vid_dec_para->media_handle))
				{
					DBG_PRINT("VidDecEnd.\r\n");
					video_decode_task_end();
					vid_dec_clear_status(C_VIDEO_DECODE_PLAYING);
					vid_dec_end_callback();
					p_vid_dec_para->pend_cnt = p_vid_dec_para->post_cnt;
					OSQFlush(vid_dec_q);
					continue;
				}

				goto EndOf_MSG_VID_DEC_ONE_FRAME;
			}

			OS_ENTER_CRITICAL();
			temp = (INT64S) (p_vid_dec_para->tv - p_vid_dec_para->Tv);
			OS_EXIT_CRITICAL();
			if(temp > p_vid_dec_para->time_range)	// check sync
			{
				if(p_vid_dec_para->fail_cnt < 2)
				{
					p_vid_dec_para->fail_cnt++;
					MultiMediaParser_SetFrameDropLevel(p_vid_dec_para->media_handle, p_vid_dec_para->fail_cnt);
					DBG_PRINT("FailCnt = %d\r\n", p_vid_dec_para->fail_cnt);
				}
			}
			else
			{
				if(p_vid_dec_para->fail_cnt > 0)
				{
					p_vid_dec_para->fail_cnt--;
					MultiMediaParser_SetFrameDropLevel(p_vid_dec_para->media_handle, p_vid_dec_para->fail_cnt);
					DBG_PRINT("FailCnt = %d\r\n", p_vid_dec_para->fail_cnt);
				}
			}

			display_frame = ap_display_queue_get(display_free_queue);
			if(display_frame == NULL)
			{
				goto EndOf_MSG_VID_DEC_ONE_FRAME_doNothing;
			}

			display_frame->offset = 0;

#if 0
			display_buf = display_frame->base + display_frame->offset;
			status = video_decode_jpeg((INT8U *) raw_data_addr, size, display_buf, tft_w, tft_h, NULL, 0);
#else
			display_buf = display_frame->base + display_frame->offset+(height_offset * 2 * tft_w);
			gp_memset((void *) display_frame->base, 0, tft_w * tft_h * 2);
			status = video_decode_jpeg((INT8U *) raw_data_addr, size, display_buf, tft_w, tft_h - height_offset, NULL, 0);
#endif
			if(status < 0)
			{
				DBG_PRINT("JpegDecFail = %d\r\n", status);
				ap_display_queue_put(display_free_queue, display_frame);
				success_flag = 1;
				goto EndOf_MSG_VID_DEC_ONE_FRAME;
			}

			success_flag = 1;
			if(display_frame != NULL && !nth_flag)
			{
				OSQPost(DisplayTaskQ, (void *) (MSG_DISPLAY_TASK_MJPEG_DRAW | (INT32U)display_frame));

				//DBG_PRINT("x");
			}

EndOf_MSG_VID_DEC_ONE_FRAME:
			if(success_flag)
			{
				OS_ENTER_CRITICAL();
				p_vid_dec_para->Tv += delta_Tv;
				OS_EXIT_CRITICAL();
			}

			// get video raw data
			raw_data_addr = (INT32U) MultiMediaParser_GetVidBuf(p_vid_dec_para->media_handle, &size, &Duration, &error_id);
			size += raw_data_addr & 3;				//align and size increase
			raw_data_addr -= raw_data_addr & 3;		//align addr
			while(error_id < 0);

			if(p_wave_info)
			{
				delta_Tv = (INT64S) Duration * p_wave_info->nSamplesPerSec * 2;
			}
			else
			{
				p_vid_dec_para->n = 1;
				delta_Tv = (INT64S) Duration * TIME_BASE_TICK_RATE;
			}

EndOf_MSG_VID_DEC_ONE_FRAME_doNothing:
			// allow next frame input
			p_vid_dec_para->pend_cnt++;
			break;

		case MSG_VID_DEC_START:
			scaler_out_addr = 0;

		case MSG_VID_DEC_RESTART:
			init_flag = 0;
			delta_Tv = 0;
			p_vid_dec_para->fail_cnt = 0;

			// pre-latch video raw data
			while(1)
			{
				raw_data_addr = (INT32U) MultiMediaParser_GetVidBuf(p_vid_dec_para->media_handle, &size, &Duration, &error_id);
				size += raw_data_addr & 3;			//align and size increase
				raw_data_addr -= raw_data_addr & 3; //align addr
				while(error_id < 0);
				if(nth_flag)
				{
					if(raw_data_addr && (size >= 10))
						break;
				}
				else
				{
					if(raw_data_addr)
						break;
				}

				OSTimeDly(1);
			}

			if(p_wave_info)
			{
				delta_Tv = (INT64S) Duration * p_wave_info->nSamplesPerSec * 2;
			}
			else
			{
				p_vid_dec_para->n = 1;
				delta_Tv = (INT64S) Duration * TIME_BASE_TICK_RATE;
			}

			switch(p_vid_dec_para->video_format)
			{
			case C_MJPG_FORMAT:
				init_flag = 1;
				break;

			default:
				do
				{
					DBG_PRINT("[ERROR] unknown format @%s:%d\r\n", __func__, __LINE__);
				}while(1);
			}

			if(nth_flag)
				break;
			OSMboxPost(vid_dec_ack_m, (void *) C_ACK_SUCCESS);
			break;

		case MSG_VID_DEC_STOP:
			if(nth_flag)
				nth_flag = 0;
			video_decode_task_end();
			OSQFlush(vid_dec_q);
			OSMboxPost(vid_dec_ack_m, (void *) C_ACK_SUCCESS);
			break;

		case MSG_VID_DEC_NTH:
			if(OSQPost(vid_dec_q, (void *) MSG_VID_DEC_START) != OS_NO_ERR)
				goto MSG_VID_DEC_NTH_Fail;

			if(OSQPost(vid_dec_q, (void *) MSG_VID_DEC_ONE_FRAME) != OS_NO_ERR)
				goto MSG_VID_DEC_NTH_Fail;

			if(OSQPost(vid_dec_q, (void *) MSG_VID_DEC_STOP) != OS_NO_ERR)
				goto MSG_VID_DEC_NTH_Fail;

			nth_flag = 1;
			continue;

MSG_VID_DEC_NTH_Fail:
			nth_flag = 0;
			OSQFlush(vid_dec_q);
			OSMboxPost(vid_dec_ack_m, (void *) C_ACK_FAIL);
			break;

		case MSG_VID_DEC_EXIT:
			#if (Debug_playback == TRUE)
			DBG_PRINT("reveice VID_DEC_EXIT \r\n");
			#endif
			OSMboxPost(vid_dec_ack_m, (void *) C_ACK_SUCCESS);
			OSTaskDel(OS_PRIO_SELF);
			break;
		}
	}
}
